import json
import re
import traceback
import requests

class HTTP:

    def __init__(self, writer, test_data): # 构造函数, 初始化实例变量/类属性; 创建对象时自动调用
        self.session = requests.session()  # 会话管理对象
        self.url = ''   # 基础URL地址, 不包含接口名称  http://10.36.173.14:38080/inter/HTTP/
        self.test_data = test_data
        self.resp = None    # 响应对象
        self.jsonres = None # 字典格式响应数据
        self.relations = {} # 存储关联数据
        self.writer = writer
        self.r = 0      # 记录读取数据的行号

    def set_url(self, url): # http://10.36.173.14:38080/inter/HTTP/
        """
        设置基础URL地址
        :param url: url数据
        :return:
        """
        try:
            self.url = url
            self.write_excel(True, str(self.url))
            # print(self.url)
            return True
        except Exception as e:
            self.write_excel(False, f"设置URL失败: {str(e)}")
            return False

    def post(self, inter_name, payload=None): # getUserInfo  payload="id={{id}}"
        """
        post请求
        :param inter_name: 接口名称
        :param payload: 参数数据
        :return:
        """
        try:
            url = self.url + inter_name
            print("请求URL: ", url)
            payload = self.__get_relation(payload) # payload = "id=1"
            payload = self.__get_data(payload) # payload=  "{'id':'1'}"
            print("请求报文: ", payload)
            try:
                self.resp = self.session.post(url, data=payload)
            except Exception as e:
                self.resp = None
                self.write_excel(False, str(traceback.format_exc()))

            if self.resp is None:
                self.jsonres = None
            else:
                try:
                    self.jsonres = json.loads(self.resp.text)
                    self.write_excel(True, str(self.jsonres))
                    return True
                except Exception as e:
                    self.jsonres = None
                    self.write_excel(False,str(self.resp.text))
                    return False
        except Exception as e:
            error_msg = f"POST请求失败: {str(e)}\n{traceback.format_exc()}"
            self.write_excel(False, error_msg)
            return False
        print("响应结果: ", self.jsonres)

    def __get_data(self,payload): #
        """
        将字符串格式的数据 转换为字典
        :param payload: 字符串数据
        :return: 字典格式数据
        """
        if payload is None:
            return None
        list1 = payload.split('&')  # form混合表单传参:  key1=value1&key2=value2..
        d = {}
        for data in list1:
            idx = data.find('=')
            key = data[:idx]
            value = data[idx+1:]
            d[key] = value
        return d

    def save_relation(self, k, v): # token  token
        """
        保存关联数据到关联字典中
        :param k: 插入数据的key
        :param v: 插入数据的value
        """
        try:
            value = self.jsonres[v]
            self.relations[k] = value
            self.write_excel(True, str(self.relations))
            return True
        except Exception as e:
            error_msg = f"保存关联数据失败: {str(e)}"
            self.write_excel(False, error_msg)
            return False

    def add_header(self, k, v): # {{token}}
        """
        请求头中添加新的键值对数据
        :param k: key
        :param v: value
        :return:
        """
        try:
            value = self.__get_relation(v)
            self.session.headers[k] = value
            self.write_excel(True, str(self.session.headers))
            return True
        except Exception as e:
            error_msg = f"添加请求头失败: {str(e)}"
            self.write_excel(False, error_msg)
            return False

    def __get_relation(self, payload): # id={{id}}  {{token}}
        """
        替换参数中关联的数据
        :param payload: 参数: 如果参数中包含一个 {{key}}, 且key存在于关联字典中,将字符串中满足该规则{{key}}的数据替换为字典中对应key的值
        :return:
        """
        if payload is None:
            return None
        for key in self.relations.keys():
            payload = payload.replace('{{' + key + '}}', self.relations.get(key))

        # 适配 替换环境测试数据
        if '{{env.' in payload:
            for key_path in re.findall(r'{{env\.(.*?)}}', payload):
                try:
                    # 支持多级路径: env.customers.cust_name
                    keys = key_path.split('.')
                    value = self.test_data
                    for k in keys:
                        value = value[k]
                    payload = payload.replace('{{env.' + key_path + '}}', str(value))
                except KeyError:
                    raise ValueError(f"测试数据不存在: env.{key_path}")

        return payload

    def assert_equal(self, k, v):
        """
        断言实际key的值与预期的value值一致
        :param k: 实际结果的key
        :param v: 预期结果
        :return:
        """
        try:
            result = str(self.jsonres[k])
            expected = str(v)
            if result == expected:
                self.write_excel(True, "预期结果与实际结果一致, 通过!")
                return True
            else:
                error_msg = f"断言失败: {result} != {expected}"
                self.write_excel(False, error_msg)
                return False
        except KeyError:
            error_msg = f"键不存在: {k}"
            self.write_excel(False, error_msg)
            return False
        except Exception as e:
            error_msg = f"断言异常: {str(e)}"
            self.write_excel(False, error_msg)
            return False

    def assert_equals(self, expect_dict):
        """
        断言期望结果字典中的每个值与实际结果字典中的每个值完全相等
        :param expect_dict:
        :return:
        """
        for key in expect_dict.keys():
            if str(expect_dict.get(key)) != str(self.jsonres.get(key)):
                print("FAIL")
                self.write_excel(False, "预期结果与实际结果不一致, 失败!")
                return False
        print('PASS')
        self.write_excel(True, "预期结果与实际结果一致, 通过!")
        return True

    def assert_contains(self, expect_str):
        """
        断言预期字符串包含在响应的文本字符串中
        :param expect_str: 预期字符串数据
        :return:
        """
        if str(self.resp.text).__contains__(str(expect_str)):
            print("PASS")
            self.write_excel(True, "预期结果与实际结果一致, 通过!")
            return True
        else:
            print('FAIL')
            self.write_excel(False, "预期结果与实际结果不一致, 失败!")
            return False

    def write_excel(self, status, msg):
        """
        反向将关键字函数执行结果写入excel中
        :param status: 执行的状态
        :param msg: 实际结果
        :return:
        """
        if status:
            self.writer.write(self.r, 8,"PASS", "3")
        else:
            self.writer.write(self.r, 8, "FAIL", "2")

        if len(msg) > 1024:
            msg = msg[:1024]
        self.writer.write(self.r, 9, msg)